import tornado.web
import jwt
import os
import json
import string
from random import sample
from datetime import datetime, timedelta
from base.LoggerHandler import logger
from base.Models import DBSession, UserDoLog
from base.DateHandler import DateHandler
from base.RedisHandler import RedisHandler
from common.error_code import TOKEN_EXPIRE
from config import config


# 无token信息验证
class BaseHandler(tornado.web.RequestHandler):
    def initialize(self) -> None:
        self.logger = logger
        self.session = DBSession()
        self.redis_conn = RedisHandler().conn

    def set_default_headers(self):
        # 后面的*可以换成ip地址，意为允许访问的地址
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Content-Type', 'application/json')
        self.set_header('Access-Control-Allow-Headers', 'Access-Control-Allow-Origin, Access-Control-Allow-Methods, '
                                                        'Access-Control-Max-Age, Content-Type, Authorization')
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT, DELETE')
        self.set_header('Access-Control-Max-Age', 100)

    def options(self):
        print("options is request")

    def prepare(self):
        self.logger.info("")
        ip = self.request.headers.get("X-Real-IP", "")
        print("ip: %s" % ip)
        self.logger.info("ip:%s, host:%s, uri:%s, method:%s " % (ip, self.request.host, self.request.uri, self.request.method))
        if self.request.method in ["GET", "OPTIONS"]:
            self.logger.info("params:%s" % self.get_params())
        else:
            if "upload" not in self.request.uri:
                self.logger.info("body:%s" % json.loads(self.request.body))

    # 请求api返回的数据格式
    @classmethod
    def result(cls, data=None, msg="操作失败！", status=0):
        res = {"status": status, "msg": msg, "data": data}
        return res

    # 时间验证
    @staticmethod
    def is_expired(last_datetime, hours=72):
        print(last_datetime)
        current_dt = datetime.now()
        last_dt = datetime.fromtimestamp(last_datetime)
        last_dt = last_dt + timedelta(hours=hours)
        # last_dt = last_dt + timedelta(seconds=10)

        return current_dt > last_dt

    def get_params(self):
        params = dict()
        for data in self.request.arguments:
            params[data] = self.get_argument(data, None)
        self.check_sql_inject(params)
        return params

    def get_body(self):
        try:
            data = json.loads(self.request.body)
            self.check_sql_inject(data)
            return data if data is not None else {}
        except Exception as e:
            print(e)
            self.write(self.result(msg="传值方式不正确！"))
            self.finish()

    # 防止sql注入
    def check_sql_inject(self, data):
        waring_keys = ["delete", "update", "inster", "select", "drop"]
        for k, v in data.items():
            for i in waring_keys:
                if i in str(v):
                    self.logger.error("""
************************************************************
此用户操作涉嫌【sql注入】
URI: %s
IP : %s
header : %s
************************************************************""" % (self.request.uri, self.request.headers.get("X-Real-IP", ""), self.request.body))
                    self.write(self.result("存在非法字符！"))
                    self.finish()

    # 自定义返回内容
    def data_except(self, msg):
        raise UserWarning(msg)

    def data_not_null(self, data, key, msg):
        value = data.get(key, None)
        if value == None:
            self.data_except(msg)
        return value

    # 验证错误信息是否包含中文
    # 包含中文的是自定义错误，否则是系统错误
    def check_except_zh(self, e, msg="操作失败！"):
        e_str = e.__str__()
        is_zh = False
        for ch in e_str:
            if u'\u4e00' <= ch <= u'\u9fff':
                is_zh = True
        result = self.result(msg=e_str) if is_zh else self.result(msg=msg)
        return result

    def get_file_path(self, dirname, file_type, year=None, month=None, day=None):
        file_name = "%s.%s" % (self.get_random_file_name(), file_type)
        if year is None or month is None or day is None:
            today = DateHandler.today().split("-")
            year, month, day = today[0], today[1], today[2]
        children_path = os.path.join("static", dirname, year, month, day)
        parent_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), children_path)
        if not os.path.exists(parent_path):
            os.makedirs(parent_path)

        file_path = parent_path + "/" + file_name
        online_path = os.path.join(config["server_name"], children_path, file_name)
        print(file_path)
        print(online_path)
        return file_path, online_path

    # 随机字符串文件名
    @staticmethod
    def get_random_file_name(str_length=9):
        return ''.join(sample(string.ascii_letters + string.digits, str_length))

    # 记录用户操作
    def record_user_operation(self, user_id, user_name, todo=None):
        try:
            new_do_log = UserDoLog(user_id=user_id, user_name=user_name, todo=todo, ip=self.request.remote_ip)
            self.session.add(new_do_log)
            # self.session.commit()
        except Exception as e:
            # self.session.rollback()
            self.logger.error(e)

    # 请求完成后的处理
    def on_finish(self) -> None:
        self.session.close()
        # pass


# 有token信息验证
class AuthHandler(BaseHandler):
    def initialize(self) -> None:
        super(AuthHandler, self).initialize()
        self.user_id = None
        self.user_name = None

    def prepare(self):
        super(AuthHandler, self).prepare()
        try:
            if self.request.method != "OPTIONS":
                token = self.request.headers.get("Authorization", None)
                if not token:
                    raise UserWarning("用户身份无效！")

                token = jwt.decode(token, config["user_key"], algorithm="HS256")
                print("token: %s" % token)
                self.user_id = token.get("user_id")
                self.user_name = token.get("user_name")

        except Exception as e:
            self.logger.error(e)
            self.write(self.result(msg="用户身份无效，请重新登录！", status=TOKEN_EXPIRE))
            return self.finish()






